5.7 Namensräume (Namespaces)  
Die .NET-Klassenbibliothek enthält ungezählte Klassendefinitionen, die dem Entwickler im Bedarfsfall ihre individuellen Dienste über Methoden bereitstellen. Es kann davon ausgegangen werden, dass sich das Angebot im Laufe der Zeit durch neue Technologien noch deutlich erweitern wird. Dabei sind die benutzerdefinierten Klassen noch nicht berücksichtigt.
Gäbe es für dieses große Angebot keine besondere Verwaltungsstruktur, wäre das Chaos perfekt. Erfahrene Entwickler wissen, wie schwierig es ist, aus den ca. 5 000 bis 6 000 verschiedenen Betriebssystemfunktionen eines 32-Bit-Windows-Betriebssystems eine bestimmte zu finden, die genau das leistet, was an Funktionalität gesucht wird. Da hilft auch kein seitens Microsoft sorgfältig gewählter, beschreibender Funktionsname weiter: Die Suche gleicht der nach der berühmten Stecknadel im Heuhaufen.
Diese Desorganisation bereitete natürlich auch den Entwicklern des Betriebssystems immer wieder aufs Neue Kopfschmerzen. Mit jeder weiteren neuen Funktion musste die Eindeutigkeit des Bezeichners gewährleistet sein, um Konflikte zu vermeiden. Die Entwickler von Anwendungen wiederum müssen sicherstellen, dass ihre eigenen globalen Funktionsnamen nicht mit denen des Betriebssystems kollidieren.
Dieser Problematik waren sich die Designer der .NET-Plattform wohl bewusst und haben das Konzept der Namespaces (Namensräume) in .NET eingeführt. Namespaces sind hierarchische, logische Organisationsstrukturen. Sie kategorisieren Klassendefinitionen, um das Auffinden einer bestimmten Funktionalität auf ein Minimum an Aufwand zu reduzieren und Mehrdeutigkeiten bei Typangaben zu vermeiden.
Namespaces lassen sich sehr gut mit der Ordnerstruktur des Dateisystems vergleichen. Dabei ähnelt ein Namespace einem Verzeichnis. Jedes Verzeichnis enthält Dateien, die meist logisch miteinander in Beziehung stehen: Beispielsweise können die Dateien eine Anwendung bilden, oder es handelt sich um gemeinsam verwaltete Benutzerdokumente. Innerhalb eines Namespace werden ebenfalls logisch zusammenhängende Klassen verwaltet. Bei dem Vergleich mit dem physikalischen Dateisystem entspricht eine Typdefinition einer Datei. Innerhalb eines Ordners muss der Name einer Datei eindeutig sein – innerhalb eines Namespace gilt dasselbe für die Typbezeichner. Im Dateisystem können Verzeichnisse Unterverzeichnisse enthalten, um eine feinere Gliederung zu erzielen. Aus denselben Gründen können Namespaces weitere Namespaces einbetten.
Ein Namespace ist ein Verwaltungskonstrukt, in dem ein oder mehrere Typen logisch gruppiert werden. Üblicherweise handelt es sich dabei um Klassen, die funktional in einer verwandtschaftlichen Beziehung stehen. Beispielsweise sind alle Klassen des .NET Frameworks, die Dateioperationen zur Verfügung stellen, dem Namespace System.IO zugeordnet. Der größte Namespace ist der mit der Bezeichnung System. Er enthält die wichtigsten .NET-Typen und hat aus organisatorischen Gründen weitere, untergeordnete Namespaces.
Zwischen einem Namespace und einer Datei, die Typdefinitionen enthält, besteht keine 1:1-Beziehung. Vielmehr kann sich ein Namespace über mehrere Dateien erstrecken. Umgekehrt können in einer Datei auch mehrere Namespaces definiert werden. Ein Datenbänkler würde von einer »Viel-zu-viele-Beziehung« sprechen.
Grundsätzlich ist jede Klasse des .NET Frameworks Mitglied eines Namespace. Folgerichtig wird auch jedweder Programmcode in Namespaces verwaltet. Jedes neue Projekt eröffnet dazu einen neuen Namespace, in dem alle Typen des aktuellen Projekts verwaltet werden.
5.7.1 Zugriff auf Namespaces  
Es ist ein Irrtum zu glauben, ohne weitere Maßnahme auf jeden beliebigen Namespace und eine darin verwaltete Klasse Zugriff zu erhalten. Vielmehr muss das Projekt die Datei, die den erforderlichen Namespace veröffentlicht, einbinden.
Damit jedes Projekt schon beim Start eine minimale Grundfunktionalität gewährleisten kann, sind die wichtigsten Dateien – letztendlich also Namespaces – von Anfang an eingebunden. Sie finden die Liste der Dateiverweise im Knoten Verweise im Projektmappen-Explorer. Dieser ist per Vorgabe zunächst ausgeblendet. Um ihn sichtbar zu machen, klicken Sie die Schaltfläche Alle Dateien anzeigen in der Symbolleiste des Projektmappen-Explorers. Danach wird der Verweisknoten angezeigt, den Sie nun öffnen können (siehe Abbildung 5.3).
 Hier klicken, um das Bild zu Vergrößern
Abbildung 5.3 Die standardmäßige Verweisliste einer Konsolenanwendung
Damit stehen Ihnen schon beim Öffnen eines neuen Projekts sehr viele Klassen mit den grundlegendsten Funktionalitäten zur Verfügung. Sollte es sich im Laufe der Entwicklungszeit herausstellen, dass darüber hinaus noch weitere benötigt werden, muss die Verweisliste ergänzt werden. Dazu ist das Kontextmenü des Knotens Verweise im Projektmappen-Explorer zu öffnen und Verweis hinzufügen... zu wählen. Daraufhin wird das in Abbildung 5.4 dargestellte Dialogfenster angezeigt.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 5.4 Dialog zum Hinzufügen eines Verweises
In der Registerkarte .NET wird die gewünschte Datei markiert und über die Schaltfläche OK der Liste der ausgewählten Komponenten hinzugefügt.
Wissen Sie, welche Klasse Sie in Ihrem Projekt benötigen, stellt sich nur noch die Frage, in welcher Datei die Klasse zu finden ist. Die Lösung ist sehr einfach, wenn Sie sich das Datenblatt der entsprechenden Klasse in der .NET-Dokumentation ansehen. Darin werden Sie sowohl die Angabe des Namespace finden, dem die Klasse zugeordnet ist, als auch die Angabe der zugehörigen Datei mit der Dateiendung .dll.
5.7.2 Die »Imports«-Anweisung  
Eigentlich muss beim Zugriff auf eine Klasse mittels Punktnotation auch der Namespace angeführt werden, dem die Klasse zugeordnet ist. Betrachten wir dazu das schon häufig benutzte Beispiel der Methode WriteLine der Klasse Console, die zum Namespace System gehört. Um im Konsolenfenster eine Ausgabe zu erhalten, muss streng genommen
| System.Console.WriteLine("Hallo Welt")
|
codiert werden. Die Angabe aus Namespace und Klassenname wird als voll qualifizierender Name bezeichnet. Voll qualifizierende Namen führen oft zu sehr langen, unübersichtlichen und schlecht lesbaren Ausdrücken im Programmcode. Visual Basic bietet uns mit der Imports-Anweisung Abhilfe. Mit
kann an späterer Stelle im Programmcode auf alle Typen des angegebenen Namespace unter Angabe des Typbezeichners zugegriffen werden, ohne den voll qualifizierenden Namen angeben zu müssen:
| Console.WriteLine("Hallo Welt")
|
Importierte Namespaces können anwendungsweit Gültigkeit haben als auch auf eine Quellcodedatei beschränkt werden. Schreiben Sie die Imports-Anweisung in eine Quellcodedatei, gilt der angegebene Namespace nur für diese Datei und den darin enthaltenen Programmcode. Dabei steht Imports immer außerhalb der Typdefinition.
Die wichtigsten Namespaces sollten anwendungsweit bekannt gemacht werden. Dazu öffnen Sie das Eigenschaftsfenster des Projekts. Sie können dazu das Kontextmenü des Projektknotens im Projektmappen-Explorer öffnen und Eigenschaften wählen, oder Sie doppelklicken den Knoten My Project im Projektmappen-Explorer. In beiden Fällen wird im Code-Editor das Eigenschaftsfenster geöffnet, das Sie in der folgenden Abbildung sehen. Markieren Sie die Lasche Verweise am linken Rand des Fensters, damit der Bereich für die Verweise und die anwendungsweiten Importe angezeigt wird.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 5.5 Die Lasche »Verweise« im Eigenschaftsfenster des Projekts
Die im oberen Bereich aufgelisteten Verweise entsprechen denen des zuvor beschriebenen Verweisknotens im Projektmappen-Explorer. Im unteren Bereich werden Ihnen die entsprechenden Namespaces zur Auswahl angeboten. Sie brauchen hier nur noch die benötigten auszuwählen, die dann für die gesamte Anwendung gültig sind.
5.7.3 Aliasnamen zur Vermeidung von Mehrdeutigkeiten  
Es gibt auch noch eine weitere Möglichkeit, um den Eindeutigkeitskonflikt oder eine überlange Namespace-Angabe zu vermeiden: die Definition eines Alias. Allgemein formuliert sieht die vollständige Syntax der Imports-Anweisung folgendermaßen aus:
| Imports [<Aliasname>] = <Namespace>
|
Ein Alias ermöglicht noch weitergehende Vereinfachungen: Während die einfache Angabe ohne Alias hinter Imports nur einen Namespace erlaubt, kann ein Alias sogar für den vollständig qualifizierenden Typbezeichner stehen.
Damit könnte die Klasse Person in den beiden Namespaces auch wie folgt genutzt werden:
| Imports FirstPerson = MyApplication.FirstCompany.Person
|
| Imports SecondPerson = YourApplication.SecondCompany.Person
|
| ...
|
| Dim obj As New FirstPerson()
|
Genauso können Sie, falls Sie Spaß daran haben, die Klasse Console »umbenennen«, z. B. in Ausgabe:
| Imports Ausgabe = System.Console
|
| ...
|
| Ausgabe.WriteLine("Hallo Welt")
|
Halten Sie sich aber dabei immer vor Augen, dass ein Alias für einen allgemein bekannten Typbezeichner schlechter interpretierbaren Code zur Folge hat. Daher sollte ein Alias wirklich nur da eingesetzt werden, wo es notwendig ist.
5.7.4 Aufrufe mit »Global« umleiten  
Ein Alias hilft auch dabei, gezielt auf Typen zuzugreifen, wenn ein gleichnamiges Member der Anwendung in dem näheren Sichtbarkeitsbereich zu finden ist. Dazu ein Beispiel:
| Imports OuterSystem = System
|
| Namespace System
|
| Module Module1
|
| Dim Console As Integer = 23
|
| Sub Main()
|
| OuterSystem.Console.WriteLine(Console)
|
| OuterSystem.Console.ReadLine()
|
| End Sub
|
| End Module
|
| End Namespace
|
In Module1, das dem Namespace System zugeordnet wird, ist das Feld Console definiert. Eine gleichnamige Klasse befindet sich auch im Namespace System von .NET. Ohne Aliasangabe würden die Anweisungen in Main zu einem Compilerfehler führen, da das Feld Console die gleichnamige Klasse überdeckt.
Ein Alias hinter Imports führt jedoch zu einem schlecht les- und wartbaren Code. Es gibt daher noch eine andere Alternative, bei Doppeldeutigkeit den Aufruf der Klasse Console an den globalen Namespace weiterleiten. Sehen wir uns die Lösung dazu an:
| Namespace System
|
| Module Module1
|
| Dim Console As Integer = 23
|
| Sub Main()
|
| Global.System.Console.WriteLine(Console)
|
| Global.System.Console.ReadLine()
|
| End Sub
|
| End Module
|
| End Namespace
|
Die Umleitung zum Namespace wird hier durch Global ermöglicht. Das Ergebnis des Aufrufs ist das gleiche wie im Codebeispiel zuvor: Es wird die WriteLine-Methode der Klasse System.Console aufgerufen und der Inhalt des Feldes Console angezeigt.
5.7.5 Namespaces festlegen  
Entwickeln Sie ein neues VB-Projekt, wird diesem von der Entwicklungsumgebung automatisch ein Namespace zugeordnet. Standardmäßig entspricht der Name des Namespace dem Namen Projektnamen. Haben Sie Ihrem Projekt den Namen MeineTolleApplikation gegeben, wird für das Projekt ein gleichnamiger Namespace eröffnet, in dem alle Klassen, Strukturen und Auflistungen definiert werden.
Sie können diesen für Klassen des Projekts gültigen Stamm-Namespace im Eigenschaftsfenster des Projekts ändern. Dazu markieren Sie die Registerkarte Anwendung.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 5.6 Die Angabe des Stamm-Namespace im Projekteigenschaftsfenster
Der Stamm-Namespace gilt für alle Typdefinitionen im Projekt. Daher finden Sie auch in den Quellcodedateien zunächst keine Namespace-Angabe. Wollen Sie mehrere Stamm-Namespaces innerhalb eines Projekts festlegen, leeren Sie das Eingabefeld und definieren im Code-Editor ausdrücklich den jeweils gewünschten Namespace.
Solange sich Typen innerhalb desselben Namespace befinden, können sie sich direkt mit ihrem Namen ansprechen. Die Klassen ClassA, ClassB und ClassC des folgenden Codefragments sind demselben Namespace zugeordnet, nämlich dem Stamm-Namespace (falls dieser im Eigenschaftsfenster einen Eintrag hat). Sie benötigen deshalb für den gegenseitigen Zugriff keine voll qualifizierende Namensangabe.
| Public Class ClassA
|
| End Class
|
| Public Class ClassB
|
| End Class
|
| Public Class ClassC
|
| End Class
|
Namespaces können beliebig verschachtelt werden, wie das folgende Beispiel zeigt:
| Namespace MyApplication
|
| Public Class ClassA
|
| End Class
|
| Public Class ClassB
|
| End Class
|
| Public Class ClassC
|
| End Class
|
| End Namespace
|
Die drei Klassen gehören jetzt dem Namespace MyApplication an. Ist im Eigenschaftsfenster noch ein Stamm-Namespace eingetragen, zum Beispiel ConsoleApplication, lautet der vollständige Bezeichner der Klasse ClassA:
| ConsoleApplication.MyApplication.ClassA
|
Der Namespace MyApplication ist nun im Namespace ConsoleApplication eingebettet. Die Verschachtelungstiefe ist grundsätzlich beliebig, sollte allerdings nicht zu viele Ebenen umfassen, um den Überblick nicht zu verlieren.
Um die diesem Namespace zugehörigen Klassen benutzen zu können, ist die folgende Imports-Anweisung notwendig:
| Imports ConsoleApplication.MyApplication
|
5.7.6 Zusammenfassung  
|
Alle Klassen des .NET Frameworks sind in Namespaces organisiert. Ein Namespace dient sowohl zum besseren Auffinden eines Typs als auch zur eindeutigen Identifizierung gleichnamiger Typen. |
|
Zwischen einem Namespace und einer Datei besteht keine 1:1-Beziehung. In einer Datei können mehrere Namespaces enthalten sein, gleichzeitig kann sich ein Namespace auch über mehrere Dateien erstrecken. |
|
Standardmäßig verlangt jede Typangabe nach dem voll qualifizierenden Bezeichner. Um zu lange Ausdrücke zu vermeiden, kann mit der Imports-Anweisung ein Pfad auf einen Namespace gelegt werden, so dass im Code nur noch der Klassenbezeichner angegeben werden muss. |
|
Optional kann der Namespace auch einem Alias zugeteilt werden. In diesem Fall darf auch die Typangabe durch den Alias beschrieben werden. |
|
Namespaces dürfen ineinander verschachtelt werden. |
|